﻿using CaseManagerLibrary.Models;
using NetSDKCS;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CaseManagerLibrary.Socket
{
    public delegate void ReceivedAsyncDUXEventHandler(string message, SocketState state);
    public delegate void SentAsyncDUXEventHandler(int sent, string message, SocketState state);
    public delegate void SocketDisconnectEventHandler(SocketState state);

    public class TcpManagerAsyncDUX
    {
        private System.Net.Sockets.Socket serverSocket;
        //private SocketState serverState;

        public event ReceivedAsyncDUXEventHandler Received;
        public event ReceivedAsyncDUXEventHandler Responsed;
        public event SentAsyncDUXEventHandler SendSuccess;
        public event SentAsyncDUXEventHandler SendFailed;

        private IPEndPoint listenEp;
        private IPEndPoint sendEp;

        private Thread socketThread;

        public event EventHandler Started;

        private Dictionary<string, SocketState> clientList;
        private Dictionary<string, SocketState> serverList;

        private static object lock_client = new object();
        private static object lock_server = new object();

        public bool IsRunning { get; private set; }


        public TcpManagerAsyncDUX(IPEndPoint listenEP, IPEndPoint sendEP)
        {
            listenEp = listenEP;
            sendEp = sendEP;

            clientList = new Dictionary<string, SocketState>();
            serverList = new Dictionary<string, SocketState>();
        }

        public void Start()
        {
            if (!IsRunning)
            {
                IsRunning = true;
                ClearConnection();
                serverSocket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                serverSocket.Bind(listenEp);
                serverSocket.Listen(0);

                serverSocket.BeginAccept(new AsyncCallback(HandleTcpClientAccepted), serverSocket);
            }
        }

        /// <summary>
        /// 停止服务器
        /// </summary>
        public void Stop()
        {
            if (IsRunning)
            {
                IsRunning = false;

                ClearConnection();
                serverSocket.Close();
                serverSocket.Dispose();
            }
        }

        private void ClearConnection()
        {
            if (clientList != null)
            {
                foreach (var conn in clientList)
                {
                    conn.Value.Close();
                }
                clientList.Clear();
            }

            if (serverList != null)
            {
                foreach (var conn in serverList)
                {
                    conn.Value.Close();
                }
                serverList.Clear();
            }
        }

        /// <summary>
        /// 处理客户端连接的函数
        /// </summary>
        /// <param name="ar"></param>
        private void HandleTcpClientAccepted(IAsyncResult ar)
        {
            if (IsRunning)
            {
                try
                {
                    var server = (System.Net.Sockets.Socket)ar.AsyncState;
                    var client = server.EndAccept(ar);

                    string address = client.RemoteEndPoint.ToString();
                    SocketState state = null;
                    lock (lock_client)
                    {
                        if (clientList.ContainsKey(address))
                        {
                            state = clientList[address];

                            if (!state.Socket.Connected)
                            {
                                state.Close();
                                state = CreateSocketState(client);
                            }
                        }
                        else
                        {
                            state = CreateSocketState(client);
                        }
                    }


                    state.ReceiveBegin(HandleDataReceived);

                    ////开始异步读取数据
                    //client.BeginReceive(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, HandleDataReceived, state);

                    server.BeginAccept(new AsyncCallback(HandleTcpClientAccepted), ar.AsyncState);
                }
                catch (Exception ex)
                { }
            }
        }

        private void HandleDataReceived(IAsyncResult ar)
        {
            try
            {
                SocketState state = (SocketState)ar.AsyncState;
                var client = state.Socket;

                int recv = 0;
                try
                {
                    recv = client.EndReceive(ar);
                }
                catch (Exception ex)
                {
                    recv = 0;
                    state.Close();
                }

                if (recv > 0)
                {
                    if (state.CurrDataLength == -1)
                    {
                        state.CurrDataLength = BitConverter.ToInt32(state.Buffer, 0);
                    }

                    state.ReceivedBytes += recv;

                    state.BufferStream.Write(state.Buffer, 0, recv);

                    if (state.ReceivedBytes >= state.CurrDataLength)
                    {
                        state.ReceiveCompleted();
                    }
                }

                if (!state.Closed)
                {
                    try
                    {
                        state.ReceiveBegin(HandleDataReceived);
                    }
                    catch (SocketException sex)
                    {
                        state.Close();
                    }
                }
            }
            catch (Exception ex)
            { }
        }

        private SocketState CreateSocketState(System.Net.Sockets.Socket client)
        {
            var state = new SocketState(client);
            state.OnDisconnected += Client_OnDisconnected;
            state.OnReceived += Client_OnReceived;
            clientList.Add(state.SessionId, state);
            return state;
        }

        private void Client_OnReceived(string message, SocketState state)
        {
            Received?.Invoke(message, state);
        }

        private void Client_OnDisconnected(SocketState state)
        {
            lock (lock_client)
            {
                clientList.Remove(state.SessionId);
            }
        }

        private SocketState TryGetClient(IPEndPoint ep)
        {
            return TryGetClient(ep.ToString());
        }

        private SocketState TryGetClient(string sessionId)
        {
            SocketState state = null;
            lock (lock_client)
            {
                if (clientList.ContainsKey(sessionId))
                {
                    state = clientList[sessionId];
                }
            }

            return state;
        }

        private SocketState TryGetServer(IPEndPoint ep)
        {
            return TryGetServer(ep.ToString());
        }

        private SocketState TryGetServer(string sessionId)
        {
            SocketState state = null;
            lock (lock_server)
            {
                //if (serverList.ContainsKey(sessionId))
                //{
                //    state = serverList[sessionId];
                //}

                if (serverList.Count > 0)
                {
                    state = serverList.FirstOrDefault().Value;
                }
            }

            return state;
        }

        public void Response(string sessionId, MessageBase msg)
        {
            string json = JsonConvert.SerializeObject(msg);

            var state = TryGetClient(sessionId);

            Response(state, json);
        }

        public void Response(SocketState state, MessageBase msg)
        {
            string json = JsonConvert.SerializeObject(msg);

            Response(state, json);
        }

        public void Response(SocketState state, string data)
        {
            HResult result = new HResult();

            try
            {
                if (state != null)
                {
                    var outputBuffer = MessageBase.Encode(data);

                    state.Socket.BeginSend(outputBuffer, 0, outputBuffer.Length, SocketFlags.None, SendDataEnd, state);
                }
                else
                {
                    SendFailed?.Invoke(0, "SocketState为空", state);
                }
            }
            catch (Exception ex)
            {
                SendFailed?.Invoke(0, ex.Message, state);
            }
        }

        public void Send(IPEndPoint remoteEP, MessageBase message, Action<int, string, SocketState> callback = null)
        {
            string json = JsonConvert.SerializeObject(message);

            Send(remoteEP, json, callback);
        }


        public void Send(IPEndPoint remoteEP, string data, Action<int, string, SocketState> callback = null)
        {
            SocketState state = null;

            try
            {

                state = TryGetServer(remoteEP);

                if (state == null)
                {
                    state = CreateServeSocketState(remoteEP);
                }

                if (!state.Socket.Connected)
                {
                    state.Connect(remoteEP);
                }

                state.SendCallback = callback;

                if (state.Socket.Connected)
                {
                    var buffer = MessageBase.Encode(data);

                   

                    state.ReceiveBegin(HandleDataReceived);

                    state.Socket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendDataEnd, state);
                }
            }
            catch (Exception ex)
            {
                SendFailed?.Invoke(0, ex.Message, state);
            }
        }

        private SocketState CreateServeSocketState(IPEndPoint remoteEP)
        {
            var socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            socket.Bind(sendEp);

            var state = new SocketState(socket);
            state.OnDisconnected += Server_OnDisconnected;
            state.OnReceived += Server_OnReceived;

            serverList.Add(remoteEP.ToString(), state);

            return state;
        }

        private void Server_OnReceived(string message, SocketState state)
        {
            Responsed?.Invoke(message, state);
        }

        private void Server_OnDisconnected(SocketState state)
        {
            lock (lock_server)
            {
                serverList.Remove(state.SessionId);
            }
        }

        private void SendDataEnd(IAsyncResult ar)
        {
            var state = (SocketState)ar.AsyncState;

            try
            {
                int sentBytes = state.Socket.EndSend(ar);

                state.Socket.Send(new byte[] { 0 });

                SendSuccess?.Invoke(sentBytes, "success", state);
            }
            catch (Exception ex)
            {
                SendFailed?.Invoke(0, ex.Message, state);
            }
        }
    }

    public class SocketState
    {
        public const int BUFFER_SIZE = 1024;
        public MemoryStream BufferStream { get; set; }
        public System.Net.Sockets.Socket Socket { get; set; }
        public byte[] Buffer { get; set; }
        public string SessionId { get; set; }
        public int CurrDataLength { get; set; }
        public int ReceivedBytes { get; set; }

        public bool Closed { get; private set; }

        public Action<int, string, SocketState> SendCallback { get; set; }

        public event ReceivedAsyncDUXEventHandler OnReceived;
        public event SocketDisconnectEventHandler OnDisconnected;

        public SocketState(System.Net.Sockets.Socket socket)
        {
            Closed = false;
            CurrDataLength = -1;
            ReceivedBytes = 0;
            Socket = socket;
            if (socket.Connected && socket.RemoteEndPoint != null)
            {
                SessionId = socket.RemoteEndPoint.ToString();
            }
        }

        public void ReceiveBegin(AsyncCallback callback)
        {
            Buffer = new byte[SocketState.BUFFER_SIZE];
            if (CurrDataLength == -1)
            {
                BufferStream = new MemoryStream();
            }
            //开始异步读取数据
            Socket.BeginReceive(Buffer, 0, Buffer.Length, SocketFlags.None, callback, this);
        }

        public void ReceiveCompleted()
        {
            if (BufferStream != null)
            {
                byte[] data = new byte[BufferStream.Length];
                BufferStream.Seek(0, System.IO.SeekOrigin.Begin);
                BufferStream.Read(data, 0, data.Length);
                BufferStream.Close();
                BufferStream.Dispose();
                BufferStream = null;

                string str = MessageBase.Decode(data);

                if (!string.IsNullOrEmpty(str))
                {
                    OnReceived?.Invoke(str, this);
                }
            }

            CurrDataLength = -1;
            ReceivedBytes = 0;

            if (!Socket.Connected)
            {
                this.Close();
            }
        }
        public void Connect(IPEndPoint remoteEP, bool startRecv = false)
        {
            Socket.Connect(remoteEP);
            SessionId = remoteEP.ToString();

            if (startRecv)
            {
                Thread t = new Thread(new ThreadStart(ReceiveData));

                t.Start();
            }
        }

        private void ReceiveData()
        {
            CurrDataLength = -1;

            while (Socket.Connected)
            {
                try
                {
                    MemoryStream ms = new MemoryStream();
                    byte[] buffer = new byte[BUFFER_SIZE];
                    int recv = Socket.Receive(buffer);
                    int total = 0;

                    if (CurrDataLength == -1)
                    {
                        CurrDataLength = BitConverter.ToInt32(buffer, 0);
                    }

                    ms.Write(buffer, 0, recv);

                    total += recv;

                    while (total < CurrDataLength + 4)
                    {
                        buffer = new byte[BUFFER_SIZE];
                        recv = Socket.Receive(buffer);

                        if (recv > 0)
                        {
                            ms.Write(buffer, 0, recv);
                            total += recv;
                        }
                    }

                    byte[] data = new byte[ms.Length];
                    ms.Seek(0, SeekOrigin.Begin);
                    ms.Read(data, 0, data.Length);
                    ms.Close();
                    ms.Dispose();

                    CurrDataLength = -1;

                    string str = MessageBase.Decode(data);

                    OnReceived?.Invoke(str, this);


                }
                catch
                {
                    break;
                }
            }
        }

        public void Close()
        {
            Closed = true;
            Socket.Shutdown(SocketShutdown.Both);
            Socket.Close();
            Socket.Dispose();
            Buffer = null;

            OnDisconnected?.Invoke(this);
        }
    }
}
